package com.learn.a04_2_unscramble.ui.test

import com.learn.a04_2_unscramble.data.MAX_NO_OF_WORDS
import com.learn.a04_2_unscramble.data.SCORE_INCREASE
import com.learn.a04_2_unscramble.data.getUnscrambledWord
import com.learn.a04_2_unscramble.ui.GameViewModel
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertTrue
import org.junit.Test
import kotlin.test.assertNotEquals

class GameViewModelTest {
    private val viewModel = GameViewModel()

    @Test
    fun gameViewModel_Initialization_FirstWordLoaded() {
        /*
         * 警告：这种方式获取uiState仅适用于MutableStateFlow。
         * 在后续课程中会学习StateFlow的高级用法，那时需要采用不同的测试方法。
         */
        val gameUiState = viewModel.uiState.value
        val unScrambledWord = getUnscrambledWord(gameUiState.currentScrambledWord)

        // 断言当前单词是混乱状态
        assertNotEquals(unScrambledWord, gameUiState.currentScrambledWord)
        // 断言当前单词计数为1
        assertTrue(gameUiState.currentWordCount == 1)
        // 断言初始分数为0
        assertTrue(gameUiState.score == 0)
        // 断言没有错误猜测
        assertFalse(gameUiState.isGuessedWordWrong)
        // 断言游戏未结束
        assertFalse(gameUiState.isGameOver)
    }

    /**
     * 测试当用户猜测错误时，错误标志是否被正确设置
     */
    @Test
    fun gameViewModel_IncorrectGuess_ErrorFlagSet() {
        // 给定一个错误的玩家输入单词
        val incorrectPlayerWord = "and"

        // 更新用户猜测并检查
        viewModel.updateUserGuess(incorrectPlayerWord)
        viewModel.checkUserGuess()

        val currentGameUiState = viewModel.uiState.value
        // 断言分数保持不变
        assertEquals(0, currentGameUiState.score)
        // 断言错误标志被正确设置
        assertTrue(currentGameUiState.isGuessedWordWrong)
    }

    /**
     * 测试当用户猜对单词时，分数是否更新且错误标志被重置
     */
    @Test
    fun gameViewModel_CorrectWordGuessed_ScoreUpdatedAndErrorFlagUnset() {
        var currentGameUiState = viewModel.uiState.value
        // 获取当前混乱单词的正确形式
        val correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)

        // 更新用户猜测并检查
        viewModel.updateUserGuess(correctPlayerWord)
        viewModel.checkUserGuess()
        currentGameUiState = viewModel.uiState.value

        // 断言错误标志被重置
        assertFalse(currentGameUiState.isGuessedWordWrong)
        // 断言分数正确更新
        assertEquals(SCORE_AFTER_FIRST_CORRECT_ANSWER, currentGameUiState.score)
    }

    /**
     * 测试跳过单词时，分数不变但单词计数增加
     */
    @Test
    fun gameViewModel_WordSkipped_ScoreUnchangedAndWordCountIncreased() {
        var currentGameUiState = viewModel.uiState.value
        val correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)

        // 先猜对一个单词
        viewModel.updateUserGuess(correctPlayerWord)
        viewModel.checkUserGuess()
        currentGameUiState = viewModel.uiState.value
        val lastWordCount = currentGameUiState.currentWordCount

        // 跳过单词
        viewModel.skipWord()
        currentGameUiState = viewModel.uiState.value
        // 断言跳过单词后分数不变
        assertEquals(SCORE_AFTER_FIRST_CORRECT_ANSWER, currentGameUiState.score)
        // 断言单词计数增加1
        assertEquals(lastWordCount + 1, currentGameUiState.currentWordCount)
    }

    /**
     * 测试所有单词猜完后UI状态是否正确更新
     */
    @Test
    fun gameViewModel_AllWordsGuessed_UiStateUpdatedCorrectly() {
        var expectedScore = 0
        var currentGameUiState = viewModel.uiState.value
        var correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)

        // 重复猜对所有单词
        repeat(MAX_NO_OF_WORDS) {
            expectedScore += SCORE_INCREASE
            viewModel.updateUserGuess(correctPlayerWord)
            viewModel.checkUserGuess()
            currentGameUiState = viewModel.uiState.value
            correctPlayerWord = getUnscrambledWord(currentGameUiState.currentScrambledWord)
            // 断言每次猜对后分数正确更新
            assertEquals(expectedScore, currentGameUiState.score)
        }
        // 断言所有问题回答后单词计数正确
        assertEquals(MAX_NO_OF_WORDS, currentGameUiState.currentWordCount)
        // 断言所有问题回答后游戏结束
        assertTrue(currentGameUiState.isGameOver)
    }

    companion object {
        // 第一个正确答案后的分数
        private const val SCORE_AFTER_FIRST_CORRECT_ANSWER = SCORE_INCREASE
    }
}